﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Package;
using Microsoft.VisualStudio.TextManager.Interop;

namespace Expect.Vs2008.Extensions.LanguageService
{
    public class ExpectScanner : IScanner
    {
        /// <summary>
        /// Store line of text to parse
        /// </summary>
        private string _sourceString;

        /// <summary>
        /// Store line of text to parse
        /// </summary>
        private string _refString;

        /// <summary>
        /// Store line of text to parse
        /// </summary>
        private string _trimmedSourceString;

        /// <summary>
        /// Store position where next token starts in line
        /// </summary>
        private int _currentPos;

        public bool ScanTokenAndProvideInfoAboutIt(TokenInfo tokenInfo, ref int state)
        {
            // If input string is empty - there is nothing to parse - so, return false
            if (_sourceString.Length == 0)
            {
                return false;
            }

            if (_trimmedSourceString.StartsWith("#"))
            {
                this.ProcessLineAs(TokenColor.String, TokenType.Comment, tokenInfo);
                return true;
            }

            if (_trimmedSourceString.StartsWith("@setup"))
            {
                this.ProcessLineAs(TokenColor.Keyword, TokenType.Keyword, tokenInfo);
                return true;
            }

            if (_trimmedSourceString.StartsWith("@specification"))
            {
                this.ProcessLineAs(TokenColor.Keyword, TokenType.Keyword, tokenInfo);
                return true;
            }

            if (_trimmedSourceString.StartsWith("@teardown"))
            {
                this.ProcessLineAs(TokenColor.Keyword, TokenType.Keyword, tokenInfo);
                return true;
            }

            return this.ProcessText(tokenInfo);
        }

        private bool ProcessText(TokenInfo tokenInfo)
        {
            int idx = _sourceString.IndexOf("|");
            if (idx < 0) return false;

            if (idx > 0)
            {
                ProcessText(idx, tokenInfo);
                return true;
            }

            if (this.isOpeningParam(idx))
            {
                int closeIdx = _sourceString.IndexOf("|", idx + 1);
                if (this.isClosingParam(closeIdx))
                {
                    this.ProcessParam(closeIdx+1, tokenInfo);
                    return true;
                }
            }

            this.ProcessParam(1, tokenInfo);
            return true;
        }

        private void ProcessParam(int length, TokenInfo tokenInfo)
        {
            tokenInfo.Color = TokenColor.Comment;
            tokenInfo.Type = TokenType.Comment;
            this.Tokenize(length, tokenInfo);
        }

        private void ProcessText(int idx, TokenInfo tokenInfo)
        {
            tokenInfo.Color = TokenColor.Text;
            tokenInfo.Type = TokenType.Text;
            this.Tokenize(idx, tokenInfo);
        }

        private bool isOpeningParam(int idx)
        {
            if (_sourceString.Length > 1)
            {
                if (_sourceString[idx + 1] != ' ') return true;
            }
            return false;
        }

        private bool isClosingParam(int idx)
        {
            if (_sourceString[idx - 1] != ' ') return true;
            return false;
        }

        private void ProcessLineAs(TokenColor color, TokenType tp, TokenInfo tokenInfo)
        {
            tokenInfo.Color = color;
            tokenInfo.Type = tp;
            this.Tokenize(_sourceString.Length, tokenInfo);
        }

        private void Tokenize(int length, TokenInfo tokenInfo)
        {
            tokenInfo.StartIndex = this._currentPos - 1;
            tokenInfo.EndIndex = tokenInfo.StartIndex + length;
            this._currentPos += length;
            this._sourceString = this._sourceString.Substring(length);
        }

        public void SetSource(string source, int offset)
        {
            _sourceString = source;
            _refString = source;
            _trimmedSourceString = source.Trim();
            _currentPos = offset;
        }
    }
}
